<template>
  <div class="u-table">
    <el-table
      ref="tableRef"
      v-loading="state.loading"
      :data="table.data"
      :max-height="maxHeight"
      stripe
      border
      :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
      row-key="id"
      :element-loading-spinner="svg"
      element-loading-svg-view-box="-10, -10, 50, 50"
      element-loading-text="拼命加载中"
      @expand-change="expandChange"
      @selection-change="handleSelectionChange"
      @header-dragend="onHeaderDragend"
    >
      <template v-for="(item, index) in columnStore" :key="index">
        <el-table-column
          v-if="item.type == 'selection'"
          type="selection"
          :align="item.align"
          :width="item.width"
        ></el-table-column>
        <el-table-column
          v-else-if="item.type == 'index'"
          type="index"
          :label="item.label"
          :align="item.align"
          :width="item.width"
        />
        <el-table-column
          v-else-if="item.type == 'img' && item.prop"
          :label="item.label"
          :width="item.width"
          :prop="item.prop"
          :align="item.align"
        >
          <template #default="scope">
            <el-image
              :src="scope.row[item.prop]"
              :preview-src-list="[scope.row[item.prop]]"
              preview-teleported
              fit="cover"
              class="w-10 h-10"
            >
              <template #error>
                <img class="w-full h-full" src="/static/img/avatar.png" alt="avatar" />
              </template>
            </el-image>
          </template>
        </el-table-column>
        <el-table-column v-else-if="item.type == 'other'" :label="item.label" :align="item.align" :width="item.width">
          <template #default="scope">
            <slot :name="item.prop" :row="scope.row"></slot>
          </template>
        </el-table-column>
        <el-table-column
          v-else-if="item.type == 'operation'"
          fixed="right"
          :align="item.align"
          label="操作"
          :width="item.width"
        >
          <template #default="scope">
            <el-link
              :underline="false"
              type="primary"
              style="font-size: 12px; margin-right: 5px"
              link
              @click="update(scope.row)"
            >
              <u-icon size="12px">
                <EditPen />
              </u-icon>
              <span>修改</span>
            </el-link>
            <el-link
              :underline="false"
              type="primary"
              :loading="state.btnLoading"
              style="font-size: 12px; margin: 0"
              link
              @click="remove([scope.row])"
            >
              <u-icon size="12px">
                <Delete />
              </u-icon>
              <span>删除</span>
            </el-link>
          </template>
        </el-table-column>
        <el-table-column
          v-else
          :label="item.label"
          :prop="item.prop"
          :align="item.align"
          :width="item.width"
          :fixed="item.fixed"
          min-width="170"
        >
          <template #default="scope">
            <!-- 判断为编辑状态 -->
            <el-input
              v-if="
                item.editor && state.tableRowEditIndex == scope.$index && state.tableColumnEditIndex == scope.column.id
              "
              id="u-table-input"
              v-model="scope.row[item.prop || '']"
              @keyup.enter="$event.target.blur()"
              @blur="onInputBlur(scope)"
            />
            <!-- 判断为显示状态 -->
            <div v-else class="row-content" @dblclick="dbClickCell(scope)">
              {{ scope.row[item.prop || ''] }}
            </div>
          </template>
        </el-table-column>
      </template>
      <slot></slot>
      <template #empty>
        <el-empty :style="{ height: emptyHeight + 'px' }" :image-size="200"></el-empty>
      </template>
    </el-table>
    <el-pagination
      v-if="table.type != 'list'"
      v-model:current-page="state.current"
      layout="total, sizes, prev, pager, next, jumper"
      small
      :hide-on-single-page="single"
      :total="total"
      :page-sizes="[5, 10, 20, 40]"
      :page-size="state.size"
      background
      class="flex flex-wrap"
      @current-change="handleCurrentChange"
      @size-change="handleSizeChange"
    ></el-pagination>
  </div>
</template>

<script setup lang="ts">
import { toPx } from '@/util'
import { EditPen, Delete } from '@element-plus/icons-vue'
import { ElMessageBox } from 'element-plus'
import 'element-plus/es/components/message-box/style/css'
import type { ElTable, ElInput } from 'element-plus'
import { debounce, cloneDeep, str, throttle, storage } from 'undraw-ui'
import { computed, nextTick, onMounted, onUnmounted, reactive, ref, toRefs, watch } from 'vue'
import { CrudApi } from './crud.vue'

export interface TableApi {
  key?: string
  type?: 'page' | 'list'
  columns: {
    label?: string
    prop?: string
    width?: number
    align?: 'left' | 'center' | 'right'
    type?: 'selection' | 'operation' | 'img' | 'other' | 'index'
    fixed?: 'left' | 'right' | false
    editor?: boolean
  }[]
  data: {}[]
  total?: number
  pageSize?: number
  single?: boolean
  number?: string
}

export interface Props {
  table: TableApi
  maxHeight?: number | string
  crud?: CrudApi
}

const props = withDefaults(defineProps<Props>(), {
  maxHeight: window.innerHeight - 300,
  pageSize: 10
})
const { single = true } = toRefs(props.table)

const tableRef = ref<InstanceType<typeof ElTable>>()

const state = reactive({
  btnLoading: false,
  loading: false, // 表格加载动画
  visible: false,
  current: 1, // 页数
  size: props.table.pageSize || 10, // 页大小
  tableRowEditIndex: undefined, // 编辑的表格行
  tableColumnEditIndex: undefined // 编辑的表格列
})

const svg = `
        <path class="path" d="
          M 30 15
          L 28 17
          M 25.61 25.61
          A 15 15, 0, 0, 1, 15 30
          A 15 15, 0, 1, 1, 27.99 7.5
          L 15 15
        " style="stroke-width: 4px; fill: rgba(0, 0, 0, 0)"/>
      `

const emptyHeight = computed(() => toPx(props.maxHeight).replace('px') - 40)

const _debounce = debounce(() => {
  state.loading = true
}, 200)

// 超出规定响应时间开启加载动画
const showLoading = () => {
  _debounce()
  return () => {
    setTimeout(() => {
      _debounce.cancel()
      state.loading = false
      tableRef.value?.clearSelection()
    }, 100)
  }
}
const update = (val: any) => {
  if (props.crud?.update) {
    props.crud.update(val)
  }
}

const remove = (val: any[]) => {
  ElMessageBox.confirm('你确定要删除吗？', '系统提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning',
    draggable: true
  }).then(() => {
    if (props.crud?.remove) {
      props.crud.remove(val, () => {
        reload()
      })
    }
  })
}

const emit = defineEmits<{
  page: [current: number, size: number, finish: () => void]
  onSelection: [val: any]
  reload: [reload: () => void]
  inputBlur: [val: any]
}>()

function handleCurrentChange(val: number) {
  state.current = val
  reload()
}

function handleSizeChange(val: number) {
  state.size = val
  reload()
}
function handleSelectionChange(val: any) {
  emit('onSelection', val)
}

const reload = throttle(() => {
  emit('page', state.current, state.size, showLoading())
})
reload()

//树形类型只展开一个
function expandChange(row: any, expanded: boolean) {
  if (expanded) {
    function tree(menu: any[]) {
      menu.forEach(item => {
        const child = item.children
        if (child) {
          tree(child)
        }
        if (row.id != item.id && row.parentId == item.parentId) {
          const child = item.children
          tableRef.value?.toggleRowExpansion(item, false)
        }
      })
    }
    tree(props.table.data)
  }
}

// --> editor input 可编辑
// 双击可编辑的单元格
const dbClickCell = (scope: any) => {
  // 找到单个格子独有的属性 - 这里是用所在行跟列id区别显示
  state.tableRowEditIndex = scope.$index
  state.tableColumnEditIndex = scope.column.id
  nextTick(() => {
    // 获取焦点
    let input = document.querySelector<HTMLInputElement>('#u-table-input')
    if (input) {
      input.focus()
      if (input.setSelectionRange) {
        input.setSelectionRange(0, input.value.length)
      }
    }
  })
}
// 表格中input取消焦点
const onInputBlur = (scope: any) => {
  state.tableRowEditIndex = undefined
  state.tableColumnEditIndex = undefined
  emit('inputBlur', scope.row)
}
// <-

const total = computed(() => props.table.total || (props.table.data && props.table.data.length))

// --> column width
let key = 'u-table-columns'
const columnStore = computed<TableApi['columns']>({
  set(val) {
    if (props.table.key) {
      let v = storage.get(key) || {}
      if (!v[props.table.key]) {
        v[props.table.key] = {}
      }
      val.forEach(t => {
        if (t.prop) {
          v[props.table.key || ''][t.prop] = t.width
        }
      })
      storage.set(key, v)
    }
  },
  get() {
    let v = props.table.columns
    if (props.table.key && storage.get(key)) {
      let v2 = storage.get(key)[props.table.key]
      if (v2) {
        v.forEach(t => {
          if (v2[t.prop || '']) {
            t.width = v2[t.prop || '']
          }
        })
      }
    }
    return v
  }
})

// 表格表头宽度改变事件
function onHeaderDragend(val: any, oldWidth: any, column: any) {
  let arr = props.table.columns
  let blacklist = ['selection']
  arr.forEach(t => {
    if (!blacklist.includes(t.type || '') && t.prop == column.property) {
      t.width = val
    }
  })
  columnStore.value = arr
}
// 重置表头宽度为默认
function resetHeaderWidth() {
  let obj = storage.get(key)
  delete obj[props.table.key || '']
  storage.set(key, obj)
}
// <-

const windowHeight = ref(props.maxHeight)

const setWindowHeight = () => {
  windowHeight.value = window.innerHeight - 300
}

function resize() {
  setWindowHeight()
}
onMounted(() => {
  window.addEventListener('resize', resize)
})

onUnmounted(() => {
  window.removeEventListener('resize', resize)
})

defineExpose({
  reload,
  resetHeaderWidth
})
</script>
<style lang="scss">
.u-table {
  .el-table .cell {
    display: flex;
    align-items: center;
    .row-content {
      min-height: 23px;
      width: 100%;
    }
  }
}
</style>
<style lang="scss" scoped>
.el-table {
  margin-bottom: 10px;
}
</style>
